package org.zhaya.sdk.processors;

import cn.hutool.core.codec.Base64;
import com.google.common.collect.Lists;
import io.logz.sawmill.Doc;
import io.logz.sawmill.ProcessResult;
import io.logz.sawmill.Processor;
import io.logz.sawmill.Template;
import io.logz.sawmill.annotations.ProcessorProvider;
import io.logz.sawmill.utilities.JsonUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.zhaya.sdk.util.BdpTemplateService;

import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;


@ProcessorProvider(type = "base64Decode", factory = Base64DecodeProcessor.Factory.class)
public class Base64DecodeProcessor implements Processor {
    private final Template path;
    private final Function<Doc, Object> getValueFunction;
    private final List<Template> pathList;
    private final List<Function<Doc, Object>> getValueFunctionList;

    public Base64DecodeProcessor(Template path, Function<Doc, Object> getValueFunction, List<Template> pathList, List<Function<Doc, Object>> getValueFunctionList) {
        this.path = path;
        this.getValueFunction = getValueFunction;
        this.pathList = pathList;
        this.getValueFunctionList = getValueFunctionList;
    }

    @Override
    public ProcessResult process(Doc doc) {
        if (CollectionUtils.isEmpty(pathList) && CollectionUtils.isEmpty(getValueFunctionList)) {
            doc.addField(path.render(doc), Base64.decodeStr(getValueFunction.apply(doc).toString()));
        } else {
            for (int i = 0; i < pathList.size(); i++) {
                doc.addField(pathList.get(i).render(doc), Base64.decodeStr(getValueFunctionList.get(i).apply(doc).toString()));
            }
        }
        return ProcessResult.success();
    }

    public static class Factory implements Processor.Factory {
        private final BdpTemplateService bdpTemplateService;

        @Inject
        public Factory(BdpTemplateService bdpTemplateService) {
            this.bdpTemplateService = bdpTemplateService;
        }

        @Override
        public Processor create(Map<String, Object> config) {
            Configuration addFieldConfig = JsonUtils.fromJsonMap(Configuration.class, config);
            Template path = null;
            Object value = null;
            List<Template> pathList = Lists.newArrayList();
            List<Object> valueList = Lists.newArrayList();
            if (CollectionUtils.isEmpty(addFieldConfig.getConfig()) && addFieldConfig.getConfig() == null) {
                path = bdpTemplateService.createTemplate(requireNonNull(addFieldConfig.getPath(), "path cannot be null"));
                value = requireNonNull(addFieldConfig.getValue(), "value cannot be null");
            } else {
                List<Map<String, Object>> addFieldsConfigList = addFieldConfig.getConfig();
                addFieldsConfigList.forEach(addFieldsConfig -> {
                    pathList.add(bdpTemplateService.createTemplate(requireNonNull((String) addFieldsConfig.get("path"), "path cannot be null")));
                    valueList.add(requireNonNull(addFieldsConfig.get("value"), "value cannot be null"));
                });
            }
            return new Base64DecodeProcessor(path, createGetValueFunction(value), pathList, createGetValueFunctionList(valueList));
        }

        private Function<Doc, Object> createGetValueFunction(Object value) {
            if (value instanceof String) {
                Template valueTemplate = bdpTemplateService.createTemplate(String.valueOf(value));
                return valueTemplate::render;
            } else {
                return (ignoredDoc) -> value;
            }
        }

        private List<Function<Doc, Object>> createGetValueFunctionList(List<Object> valueList) {
            List<Function<Doc, Object>> functionList = Lists.newArrayList();
            valueList.forEach(value -> {
                if (value instanceof String) {
                    Template valueTemplate = bdpTemplateService.createTemplate(String.valueOf(value));
                    functionList.add(valueTemplate::render);
                } else {
                    functionList.add(ignoredDoc -> value);
                }
            });
            return functionList;
        }
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Configuration implements Processor.Configuration {
        private String path;
        private Object value;
        private List<Map<String, Object>> config;

    }
}